import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:rive_common/rive_audio.dart';

// Mock implementations for testing
class MockAudioEngine implements AudioEngine {
  @override
  int channels;
  @override
  int sampleRate;
  @override
  int timeInFrames = 0;
  
  List<MockAudioSound> sounds = [];
  bool _isMonitoring = false;
  Map<int, double> _levels = {};

  MockAudioEngine(this.channels, this.sampleRate);

  @override
  void dispose() {
    sounds.forEach((sound) => sound.dispose());
    sounds.clear();
  }

  @override
  AudioSound play(AudioSource source, int engineStartTime, int engineEndTime, int soundStartTime) {
    final sound = MockAudioSound();
    sounds.add(sound);
    return sound;
  }

  @override
  void monitorLevels() {
    _isMonitoring = true;
  }

  @override
  double level(int channel) {
    return _levels[channel] ?? 0.0;
  }

  // Helper method for testing
  void setLevel(int channel, double value) {
    _levels[channel] = value;
  }

  bool get isMonitoring => _isMonitoring;
}

class MockAudioSound implements AudioSound {
  @override
  double volume = 1.0;
  @override
  bool completed = false;
  bool _disposed = false;

  @override
  void stop({Duration fadeTime = Duration.zero}) {
    completed = true;
  }

  @override
  void dispose() {
    _disposed = true;
  }

  bool get disposed => _disposed;
}

class MockAudioSource implements AudioSource {
  @override
  int sampleRate;
  @override
  int channels;
  @override
  AudioFormat format;
  bool _disposed = false;

  MockAudioSource({this.sampleRate = 44100, this.channels = 2, this.format = AudioFormat.wav});

  @override
  void dispose() {
    _disposed = true;
  }

  bool get disposed => _disposed;
}

class MockStreamingAudioSource extends MockAudioSource implements StreamingAudioSource {
  MockStreamingAudioSource() : super(format: AudioFormat.buffered);

  @override
  Future<BufferedAudioSource> makeBuffered({int? channels, int? sampleRate}) {
    return Future.value(MockBufferedAudioSource(
      sampleRate: sampleRate ?? this.sampleRate,
      channels: channels ?? this.channels,
    ));
  }
}

class MockBufferedAudioSource extends MockAudioSource implements BufferedAudioSource {
  @override
  late final Float32List samples;
  @override
  late final int length;
  
  @override
  Duration get duration =>
      Duration(microseconds: (length / sampleRate / 1e-6).floor());

  MockBufferedAudioSource({int sampleRate = 44100, int channels = 2, int sampleCount = 1000}) 
    : super(sampleRate: sampleRate, channels: channels, format: AudioFormat.buffered) {
    samples = Float32List(sampleCount);
    // Fill with test data
    for (int i = 0; i < sampleCount; i++) {
      samples[i] = i % 2 == 0 ? 0.5 : -0.5;
    }
    length = sampleCount;
  }
}

void main() {
  test('AudioFormat enum values are accessible', () {
    // Test that enum values exist and can be accessed
    expect(AudioFormat.values.length, 6);
    expect(AudioFormat.unknown, isNotNull);
    expect(AudioFormat.wav, isNotNull);
    expect(AudioFormat.flac, isNotNull);
    expect(AudioFormat.mp3, isNotNull);
    expect(AudioFormat.vorbis, isNotNull);
    expect(AudioFormat.buffered, isNotNull);
  });

  test('AudioEngine constants are correct', () {
    expect(AudioEngine.playbackSampleRate, 44100);
    expect(AudioEngine.playbackSampleRateForOhos, 48000);
  });

  test('MockAudioEngine implements all required methods', () {
    final engine = MockAudioEngine(2, 44100);
    expect(engine.channels, 2);
    expect(engine.sampleRate, 44100);
    expect(engine.timeInFrames, 0);
    
    // Test monitor levels functionality
    engine.monitorLevels();
    expect(engine.isMonitoring, true);
    
    // Test level functionality
    engine.setLevel(0, 0.5);
    expect(engine.level(0), 0.5);
    expect(engine.level(1), 0.0); // Default value for unset channel
    
    // Test sound playback
    final source = MockAudioSource();
    final sound = engine.play(source, 0, 1000, 0);
    expect(sound, isNotNull);
    expect(engine.sounds.length, 1);
    
    // Test disposal
    engine.dispose();
    expect(engine.sounds.isEmpty, true);
  });

  test('MockAudioSound implements all required methods', () {
    final sound = MockAudioSound();
    expect(sound.volume, 1.0);
    expect(sound.completed, false);
    expect(sound.disposed, false);
    
    // Test volume control
    sound.volume = 0.75;
    expect(sound.volume, 0.75);
    
    // Test stopping
    sound.stop();
    expect(sound.completed, true);
    
    // Test disposal
    sound.dispose();
    expect(sound.disposed, true);
  });

  test('MockAudioSource implements all required methods', () {
    final source = MockAudioSource(sampleRate: 48000, channels: 1, format: AudioFormat.mp3);
    expect(source.sampleRate, 48000);
    expect(source.channels, 1);
    expect(source.format, AudioFormat.mp3);
    expect(source.disposed, false);
    
    // Test disposal
    source.dispose();
    expect(source.disposed, true);
  });

  test('MockStreamingAudioSource can create buffered source', () async {
    final streamingSource = MockStreamingAudioSource();
    expect(streamingSource.format, AudioFormat.buffered);
    
    // Test creating buffered source with default parameters
    final defaultBufferedSource = await streamingSource.makeBuffered();
    expect(defaultBufferedSource, isNotNull);
    expect(defaultBufferedSource.sampleRate, 44100);
    expect(defaultBufferedSource.channels, 2);
    
    // Test creating buffered source with custom parameters
    final customBufferedSource = await streamingSource.makeBuffered(sampleRate: 48000, channels: 1);
    expect(customBufferedSource.sampleRate, 48000);
    expect(customBufferedSource.channels, 1);
  });

  test('MockBufferedAudioSource has correct properties and duration calculation', () {
    const sampleCount = 100;
    const sampleRate = 44100;
    final bufferedSource = MockBufferedAudioSource(
      sampleRate: sampleRate,
      channels: 2,
      sampleCount: sampleCount
    );
    
    expect(bufferedSource.length, sampleCount);
    expect(bufferedSource.samples.length, sampleCount);
    expect(bufferedSource.samples[0], 0.5);
    expect(bufferedSource.samples[1], -0.5);
    
    // Verify duration calculation is approximately correct
    final expectedDurationMs = (sampleCount / sampleRate * 1000).floor();
    expect(bufferedSource.duration.inMilliseconds, expectedDurationMs);
    expect(bufferedSource.duration.inMilliseconds, greaterThan(0));
  });

  test('Audio components can work together in integration', () async {
    final engine = MockAudioEngine(2, 44100);
    final streamingSource = MockStreamingAudioSource();
    
    // Convert streaming source to buffered
    final bufferedSource = await streamingSource.makeBuffered();
    
    // Play the sound
    final sound = engine.play(bufferedSource, 0, 1000, 0);
    expect(sound, isNotNull);
    expect(engine.sounds.length, 1);
    
    // Control the sound
    sound.volume = 0.5;
    expect(sound.volume, 0.5);
    
    sound.stop();
    expect(sound.completed, true);
    
    // Clean up
    sound.dispose();
    engine.dispose();
    bufferedSource.dispose();
    streamingSource.dispose();
  });
}